<template>
  <!-- 创建时间：2023/5/9 -->
  <!-- 作者：admin -->
  <!-- Scrollbar组件说明： 自定义滚动条组件-->
    <div>
        <transition name="fade">
            <div v-show="isShow"
                 id="scrollbar"
                 @click.stop="handleClick">
                <div id="thumbContainer"
                     :style="thumbStyle"
                     @mouseenter="handleMouseEnter"
                     @mouseleave="handleMouseLeave"
                     @mousedown="handleDragStart"
                     @click.stop>
                    <div :class="{active}"></div>
                </div>
            </div>
        </transition>
    </div>
</template>

<script>
export default {
    name: "Scrollbar",
    data() {
        return {
            isShow: false,
            // 隐藏定时器
            hideTimer: null,
            // 鼠标是否在滚动条上
            active: false,
            top: 0,
            thumbHeight: 0,
            isDrag: false,
            dragClientY: 0,
        }
    },
    computed: {
        // 获取父组件中主要滚动的区域
        mainParent() {
            return this.$parent.$refs.mainParent
        },
        thumbStyle() {
            return {
                transform: `translateY(${this.top}px)`,
                height: `${this.thumbHeight}px`
            }
        },
        heightOffset() {
            const {isShowBottom} = this.$store.state
            return isShowBottom ? 128 : 64
        }
    },
    methods: {
        handleScroll() {
            // clientHeight浏览器视口高度(不包括边框，边距或滚动条)
            // scrollHeight可以滚动区域的内容高度
            // scrollTop表示滚动条滚动的距离，scrollHeight = scrollTop + clientHeight
            const {clientHeight, scrollHeight, scrollTop} = this.mainParent
            const {heightOffset} = this
            // 128为顶部和底部栏的高度
            const noBottomClientHeight = clientHeight - heightOffset
            const noBottomScrollHeight = scrollHeight - heightOffset
            // 计算滚动条在可视区域应该滚动的距离
            let top = noBottomClientHeight * (scrollTop / noBottomScrollHeight)
            // 滚动条的高度随内容高度变化
            let thumbHeight = noBottomClientHeight * (clientHeight / noBottomScrollHeight)
            // 滚动条最短为24
            thumbHeight = Math.max(24, thumbHeight)
            top = Math.min(noBottomClientHeight - thumbHeight, top)
            this.top = top
            this.thumbHeight = thumbHeight
            if (!this.isShow && noBottomClientHeight !== thumbHeight) {
                this.isShow = true
            }
            this.setScrollbarHIdTimeout()
        },
        // 鼠标移入的时候显示滚动条
        handleMouseEnter() {
            this.active = true
        },
        // 鼠标离开时隐藏滚动条，并且间隔一定事件后隐藏
        handleMouseLeave() {
            this.active = false
            this.setScrollbarHIdTimeout()
        },
        setScrollbarHIdTimeout() {
            // 防抖动，每次隐藏的时候先清除定时器
            if (this.hideTimer) {
                clearTimeout(this.hideTimer)
            }
            this.hideTimer = setTimeout(() => {
                // 如果鼠标没在滚动条上就4s后隐藏
                if (!this.active) {
                    this.isShow = false
                }
                this.hideTimer = null
            }, 4000)
        },
        // 点击滚动条区域上下滑动
        handleClick(e) {
            const {clientY} = e
            let scrollTop
            if (clientY < this.top + 84) {
                scrollTop = -256
            } else {
                scrollTop = 256
            }
            this.mainParent.scrollBy({
                top: scrollTop,
                behavior: 'smooth'
            })
        },
        handleDragStart(e) {
            const {clientY} = e
            this.dragClientY = clientY
            this.isDrag = true
            document.addEventListener('mousemove', this.handleDragMove);
            document.addEventListener('mouseup', this.handleDragEnd);
        },
        handleDragMove(e) {
            if (!this.isDrag) {
                return false
            }
            const {clientY} = e
            const {clientHeight, scrollHeight, scrollTop} = this.mainParent
            const {heightOffset} = this
            const noBottomClientHeight = clientHeight - heightOffset
            const noBottomScrollHeight = scrollHeight - heightOffset
            const offset = ((clientY - this.dragClientY) / noBottomClientHeight) * noBottomScrollHeight
            // 防止覆盖到底部栏
            this.top = Math.min(noBottomClientHeight - this.thumbHeight, (scrollTop / noBottomScrollHeight) * noBottomClientHeight)
            this.mainParent.scrollBy(0, offset)
            this.dragClientY = clientY
        },
        handleDragEnd() {
            this.isDrag = false
            document.removeEventListener('mousemove', this.handleDragMove);
            document.removeEventListener('mouseup', this.handleDragEnd);
        }
    }
}
</script>

<style scoped lang="less">
.fade-enter-active, .fade-leave-active {
  transition: opacity .2s;
}

.fade-enter, .fade-leave-to {
  opacity: 0;
}

#scrollbar {
  position: fixed;
  right: 0;
  top: 0;
  bottom: 0;
  width: 16px;
  z-index: 20;
  //background-color: #cccccc;

  #thumbContainer {
    margin-top: 64px;

    div {
      transition: background 0.4s;
      position: absolute;
      right: 2px;
      width: 8px;
      height: 100%;
      border-radius: 4px;
      background: rgba(128, 128, 128, 0.38);
    }

    .active {
      background: rgba(128, 128, 128, 0.58);
    }
  }
}
</style>